package pl.edu.pw.ee.buildcity.scheduler.jobs;

import org.apache.log4j.Logger;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.StatefulJob;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.util.Assert;
import pl.edu.pw.ee.buildcity.commons.Const;
import pl.edu.pw.ee.buildcity.scheduler.BuildContext;
import pl.edu.pw.ee.buildcity.scheduler.strategies.BuildNotRequiredStrategy;
import pl.edu.pw.ee.buildcity.scheduler.strategies.BuildStrategy;
import pl.edu.pw.ee.buildcity.scheduler.strategies.CodeChangedBuildStrategy;
import pl.edu.pw.ee.buildcity.scheduler.strategies.FreshBuildStrategy;
import pl.edu.pw.ee.buildcity.scm.subversion.SubversionService;

import javax.annotation.Resource;
import java.io.File;

/**
 * @author mgrzechocinski
 * @since 2009-10-18
 */
// TODO - maybe InterruptableJob to let user stop running job?
@Configurable
public class BuildJob implements StatefulJob {

	private static final Logger log = Logger.getLogger(BuildJob.class);

	@Resource(name = "subversionService")
	private SubversionService svnService;

	/**
	 * Runs build according to given job name.
	 * Gets {@link pl.edu.pw.ee.buildcity.scheduler.BuildContext} from Quartz context, as
	 * this objects stores all necessary information to perform build as is filled
	 * with additional data during build process.
	 * <p>
	 * Given working copy might not exist, code should be updated or code is synchronized -
	 * strategy pattern is used to perform appropriate build operations.
	 * </p>
	 * <p/>
	 * Default strategy is {@link pl.edu.pw.ee.buildcity.scheduler.strategies.BuildStrategy#performBuild()}
	 * I may be overridden if it occur that working copy does not exist or code is out-of-date.
	 * <p/>
	 */
	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {

		BuildContext buildContext = extractBuildContext(context);
		File workingCopy = new File(buildContext.getWorkingCopyLocation());

		BuildStrategy buildStrategy = chooseBuildStrategy(buildContext, workingCopy);
		buildStrategy.execute();
	}

	private BuildStrategy chooseBuildStrategy(BuildContext ctx, File workingCopy) {

		//By default, we do nothing
		BuildStrategy buildStrategy = new BuildNotRequiredStrategy(ctx, workingCopy);

		//Fresh checkout - working copy does not exist or clean build is requested
		if (workingCopyNotFound(workingCopy) || ctx.isForceCleanBuildEnabled()) {
			buildStrategy = new FreshBuildStrategy(ctx, workingCopy);

		} else if (workingCopyIsOutOfDate(workingCopy)) {
			//Most common usage - code has changed, we do only need to update it (no fresh checkout required)
			buildStrategy = new CodeChangedBuildStrategy(ctx, workingCopy);
		}
		log.info("Chosen " + buildStrategy.getClass().getSimpleName() + " build strategy for working copy: " + workingCopy);
		Assert.notNull(buildStrategy.getBuildActivityService(), "Strategy class was not weaved! Please recompile all your classes using maven");
		return buildStrategy;
	}

	private boolean workingCopyNotFound(File workingCopy) {
		return !workingCopy.exists();
	}

	private boolean workingCopyIsOutOfDate(File workingCopy) {
		return svnService.changeDetected(workingCopy);
	}

	private BuildContext extractBuildContext(JobExecutionContext context) {
		return (BuildContext) context.get(Const.JOB_CONTEXT_KEY_BUILD_CONTEXT);
	}
}
